#if HACK_HARDCODED_COMPILE_UNIT_ADDED_CPP == 0
#include "iConfig.hpp"
#endif

#include <glf/io/archiveManager.h>
#include <glf/core/zip_reader.h>
#include <glf/io/memoryStream.h>

#include <algorithm>

namespace glf {

#define AM_DEBUG_TRACE //printf

static const int WORK_STR_BUFFER_SIZE = 1024;

GLF_DEFINE_SINGLETON_PLACEMENT_NEW(ArchiveManager)

//////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////
ArchiveManager::ArchiveManager() 
	: m_isArchiveGlobally(false)
	, m_archives()
{

}


//////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////
ArchiveManager::~ArchiveManager()
{
    CleanArchives();
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////
void ArchiveManager::CleanArchives()
{
    for(ArchivesIter iter = m_archives.begin(); iter != m_archives.end(); ++iter)
    {
        if ((*iter).m_archive)
            delete (*iter).m_archive;

        if ((*iter).m_archiveFile)
            delete (*iter).m_archiveFile;
    }

    m_archives.clear();
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////
bool ArchiveManager::LoadArchive( const char* filename, const char* headerfile, glf::ios::openflag flag )
{
    glf::FileStream* archiveFile = new glf::FileStream();
    bool isOpen = archiveFile->Open( filename, ios::read | flag );
    if (isOpen)
    {
        ArchiveInfo newArchive;
        newArchive.m_archive = new glf::core::CZipReader(archiveFile, filename, false, false, headerfile == 0);
        newArchive.m_archiveFile = archiveFile;

		if( headerfile )
        {
			glf::FileStream hfile;
			if(hfile.Open( headerfile, ios::read | ios::loc_raw ))
			{
				glf::MemoryStream memhfile( hfile );
				newArchive.m_archive->ImportHeader( memhfile );
			}
			else
			{
				AM_DEBUG_TRACE( "Could not load zip header file %s\n", headerfile);
			}
		}

        m_archives.push_back(newArchive);

		return true;
    }
    else
	{
        delete archiveFile;
        archiveFile = 0;

		AM_DEBUG_TRACE( "Could not load zip file %s\n", filename);
	}

	return false;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////
void ArchiveManager::CloseArchive( const char* filename )
{
    for(ArchivesIter iterBegin = m_archives.begin(); iterBegin != m_archives.end(); ++iterBegin)
    {
        if (strcmp((*iterBegin).m_archive->getZipFileName(), filename) == 0)
        {
            if ((*iterBegin).m_archive)
				delete (*iterBegin).m_archive;

			if ((*iterBegin).m_archiveFile)            
				delete (*iterBegin).m_archiveFile;

            break;
        }
    }
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////
int ArchiveManager::GetFilesInDirectory( const char* dir, glf::core::CZipReader::FileListing& outFiles )
{
	char workBuffer[WORK_STR_BUFFER_SIZE];
    int foundFileCount = 0;
    if (!m_archives.empty())
    {
        CleanPaths( dir, workBuffer, WORK_STR_BUFFER_SIZE);

        // Don't add two files with exactly the same name when there are more than one archive loaded.
        for(ArchivesIter arcIter = m_archives.begin(); arcIter != m_archives.end(); ++arcIter)
        {
            if ((*arcIter).m_archive)
            {
                glf::core::CZipReader::FileListing currentOutFiles;
		        (*arcIter).m_archive->GetFilesInDirectory(workBuffer, currentOutFiles);

                for(FileListingIter curIter = currentOutFiles.begin(); curIter != currentOutFiles.end(); ++curIter)
                {
                    FileListingIter foundIter = std::find(outFiles.begin(), outFiles.end(), (*curIter));
                    if (foundIter == outFiles.end())
                    {
                        outFiles.push_back((*curIter));
                        ++foundFileCount;
                    }
                }
            }
        }
    }

    return foundFileCount;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////
bool ArchiveManager::GetArchiveForFile( const char* filename, int& outOffset, int& outSize, const char*& outZipName  )
{
	char workBuffer[WORK_STR_BUFFER_SIZE];
    if (!m_archives.empty())
    {
		CleanPaths( filename, workBuffer, WORK_STR_BUFFER_SIZE);
        
        //end game specific hack.
		AM_DEBUG_TRACE("checking for path %s\n", workBuffer);
        
        for(ArchivesIter iter = m_archives.begin(); iter != m_archives.end(); ++iter)
        {
            ArchiveInfo archive = (*iter);
            if( archive.m_archive && archive.m_archive->getFileInfo( workBuffer, outOffset, outSize ) )
		    {
				outZipName = archive.m_archive->getZipFileName();
				return true;
		    }
        }
    }

	return false;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////
bool ArchiveManager::DoesFileExist(const char* filename)
{
	//as of now GetArchiveForFile will not that that much more processing then needed to test if file exist.
	int outOff;
	int outSize;
	const char* outName;
	return GetArchiveForFile(filename, outOff, outSize, outName);
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////
void ArchiveManager::CleanPaths(const char* inPath, char* outPath, int outPathSize)
{
	int strLen = strlen( inPath );
	
	if( outPathSize <= 0)
		return; //you gave me no buffer to work with you r-Tard.

	if( strLen >= outPathSize )
	{
		outPath[0] = '\0';	//inPath is too large, nothing we can do...
		return;
	}

	//skip dot space or slashes at the begining of path (no path in zip file will ever start by that crap)
	int index = 0;
	while(  index < strLen &&
			( inPath[index] == '/'
			|| inPath[index] == '\\'	//not needed since we just replaced them...
			|| inPath[index] == ' '
			|| inPath[index] == '.' 
			)
		)
	{
		index++;	
	}

	//copy it now
	strcpy( outPath, inPath + index );

	int strSize = strLen - index;
	for(int i = 0; i < strSize; i++)
	{
		if( outPath[i] == '\\')
		{
			outPath[i] = '/';
		}
	}	
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////
bool ArchiveManager::IsUsingArchive(int flag) const
{
	return m_isArchiveGlobally || (flag & ios::use_archive) != 0;
}

} //namespace glf
